package com.situ.mall.product.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.situ.mall.common.util.ExcelUtil;
import com.situ.mall.pojo.entity.order.OrderItem;
import com.situ.mall.pojo.entity.product.Category;
import com.situ.mall.pojo.entity.product.Product;
import com.situ.mall.product.listener.ProductExcelListener;
import com.situ.mall.product.mapper.CategoryMapper;
import com.situ.mall.product.mapper.ProductMapper;
import com.situ.mall.product.pojo.query.ProductQuery;
import com.situ.mall.product.pojo.vo.ProductCountVO;
import com.situ.mall.product.pojo.vo.ProductExcelVO;
import com.situ.mall.product.pojo.vo.ProductVO;
import com.situ.mall.product.service.IProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 商品 服务实现类
 * </p>
 *
 * @author Sun Huijuan
 * @since 2024-10-09
 */
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements IProductService {
	@Autowired
	private ProductMapper productMapper;
	@Autowired
	private CategoryMapper categoryMapper;
	@Autowired
	private RedisTemplate redisTemplate;
	
	@Autowired
	private RedissonClient redissonClient;
	
	
	@Override
	public IPage<ProductVO> selectList(ProductQuery productQuery) {
		//创建QueryWrapper搜索对象，判断参数不为空传入参数
		/*QueryWrapper<Map<String,Object>> wrapper=new QueryWrapper<>();
		wrapper.like(!ObjectUtils.isEmpty(productQuery.getName()), "p.name", productQuery.getName());
		wrapper.eq(!ObjectUtils.isEmpty(productQuery.getCategory1Id()), "p.category1_id", productQuery.getCategory1Id());
		wrapper.eq(!ObjectUtils.isEmpty(productQuery.getCategory2Id()), "p.category2_id", productQuery.getCategory2Id());
		wrapper.eq(!ObjectUtils.isEmpty(productQuery.getCategory3Id()), "p.category3_id", productQuery.getCategory3Id());
		wrapper.between(!ObjectUtils.isEmpty(productQuery.getBeginUpdateTime())&&!ObjectUtils.isEmpty(productQuery.getEndUpdateTime()),
				"p.update_time",productQuery.getBeginUpdateTime(),productQuery.getEndUpdateTime());
		wrapper.eq("p.deleted",0);
		//创建分页对象
		Page<Map<String, Object>> page=new Page<>(productQuery.getPage(),productQuery.getLimit());
		return productMapper.list(page,wrapper);*/
		
		IPage<ProductVO> page=new Page<>(productQuery.getPage(),productQuery.getLimit());
		page=productMapper.list(page,productQuery);
		return page;
	}
	
	@Override
	public List<Product> selectPromoProduct(String categoryName) {
		QueryWrapper<Category> categoryQueryWrapper=new QueryWrapper<>();
		categoryQueryWrapper.eq("name",categoryName);
		Category category=categoryMapper.selectOne(categoryQueryWrapper);
		QueryWrapper<Product> productQueryWrapper=new QueryWrapper<>();
		productQueryWrapper.eq("category3_id",category.getId())
				.eq("status",1)
				.orderByDesc("sales")
				.last("limit 7");
		List<Product> products=productMapper.selectList(productQueryWrapper);
		return products;
	}
	
	@Override
	public List<Product> getHotProduct(List<String> categoryList) {
		List<Long> categoryIds=new ArrayList<>();
		for(String categoryName:categoryList){
			QueryWrapper<Category> categoryQueryWrapper=new QueryWrapper<>();
			categoryQueryWrapper.eq("name",categoryName);
			Category category=categoryMapper.selectOne(categoryQueryWrapper);
			if(category!=null){
				categoryIds.add(category.getId());
			}
		}
		System.out.println("id数组："+categoryIds);
		QueryWrapper<Product> productQueryWrapper=new QueryWrapper<>();
		productQueryWrapper.in("category3_id",categoryIds)
				.eq("status",1)
				.orderByDesc("sales")
				.last("limit 7");
		List<Product> products=productMapper.selectList(productQueryWrapper);
		return products;
	}
	
	//加锁：sync= true。解决缓存击穿问题
	@Cacheable(value = "product",key = "#root.methodName + ':' + #id",sync = true)
	@Override
	public Product selectById(Long id) {
		return productMapper.selectById(id);
	}
	
	@CacheEvict(value = "product",key = "'selectById:'+#product.id")
	@Override
	public void update(Product product) {
		productMapper.updateById(product);
	}
	
	@Override
	public void payment(List<OrderItem> orderItemList) {
		orderItemList.forEach(item->{
			Product product=productMapper.selectById(item.getProductId());
			int oldNum=product.getStock();
			Product payProduct=new Product();
			payProduct.setId(product.getId());
			payProduct.setStock(oldNum-item.getQuantity());
			productMapper.updateById(payProduct);
		});
	}
	
	@Override
	public List<ProductCountVO> selectHotProduct() {
		return productMapper.selectHotProduct();
	}
	
	@Override
	public List<Product> selectProductList(Long id) {
		return productMapper.selectProductList(id);
	}
	
	
	public Product selectById3(Long id) {
		System.out.println("ProductServiceImpl.selectById");
		//1.先去缓存中取
		Product product = (Product) redisTemplate.opsForValue().get("product_" + id);
		//2.缓存中没有
		if (product == null) {
			redissonClient.getLock("product_lock_" + id).lock();
			try {
				//去缓存里面再次确认是不是已经有了
				product = (Product) redisTemplate.opsForValue().get("product_" + id);
				if (product == null) {//双重检查
					product = productMapper.selectById(id);
					if (product == null) {
						//不存在id恶意攻击，缓存空数据，同时设置过期时间，解决缓存穿透问题
						redisTemplate.opsForValue().set("product_" + id, new Product(), 1, TimeUnit.MINUTES);
					} else {
						//真实的缓存数据设置随机过期时间，解决缓存雪崩问题
						redisTemplate.opsForValue().set("product_" + id, product, 1, TimeUnit.DAYS);
					}
				}
			} finally {
				redissonClient.getLock("product_lock_" + id).unlock();
			}
		}
		return product;
	}
	
	private Map<String,Product> map=new HashMap<>();
	//Redis
	//asynchronize异步   synchronized同步  锁住整个方法，解决单机下的缓存击穿问题，但是引起严重问题
	//有问题的情况其实只有redis缓存没有数据的时候才需要加锁，但是因为加到方法上，
	//后面Redis中已经建立缓存product已经有值，可以直接从Redis中查找出来，
	//但是每次还是只能有一个线程访问，并发度就会大大降低
	public synchronized Product selectById2(Long id) {
		System.out.println("ProductServiceImpl.selectById");
		//1.先去缓存中取
		Product product=(Product)redisTemplate.opsForValue().get("product_"+id);
		//2.缓存中没有
		if(product==null){
			//多个线程同时进了if，但只有一个线程拿到锁。
			synchronized (this){
				//再次判断缓存中有无信息
				product=(Product)redisTemplate.opsForValue().get("product_"+id);
				//查找数据库
				if(product==null){
					System.out.println("查数据库");
					product=productMapper.selectById(id);
					//同步到缓存
					if(product==null){
						//字符串类型
						//不存在id恶意攻击，缓存空数据，同时设置过期时间，解决缓存穿透
						redisTemplate.opsForValue().set("product_"+id,new Product(),1, TimeUnit.MINUTES);
					}else{
						//真实缓存数据设置随机过期时间，解决缓存雪崩
						redisTemplate.opsForValue().set("product_"+id,product,1,TimeUnit.DAYS);
					}
				}
			}
		}
		return product;
	}
	
	public Product selectById1(Long id) {
		System.out.println("===="+map);
		//1.先去缓存中取
		Product product = map.get("product_" + id);
		//2.缓存中没有
		if(product==null){
			//查找数据库
			product=productMapper.selectById(id);
			//同步到缓存
			map.put("product_" + id,product);
			System.out.println("-----"+map);
		}
		return product;
	}
	
	@Override
	public void exportExcel(HttpServletResponse response) {
		List<ProductVO> list=productMapper.selectAll();
		List<ProductExcelVO> productExcelVOList = new ArrayList<>();
		for (ProductVO productVO : list) {
			ProductExcelVO productExcelVO = new ProductExcelVO();
			BeanUtils.copyProperties(productVO, productExcelVO);
			productExcelVOList.add(productExcelVO);
		}
		ExcelUtil.exportExcel(response,productExcelVOList,ProductExcelVO.class,"用户");
	}
	
	@Override
	public void importExcel(MultipartFile file) {
		try {
			EasyExcel.read(file.getInputStream(),ProductExcelVO.class,new ProductExcelListener(productMapper,categoryMapper)).sheet().doRead();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	
}
